// file: driver/view/core/refrsh.c
// autor: jiangxinpeng
// time: 2022.1.6
// copyright:(C) 2020-2050 by jiangxinpeng,All right are reserved.

#include <os/spinlock.h>
#include <driver/view/view.h>
#include <driver/view/hal.h>
#include <driver/view/refresh.h>
#include <driver/view/screen.h>
#include <driver/view/color.h>
#include <lib/list.h>
#include <lib/stdio.h>
#include <lib/stdlib.h>
#include <lib/stdint.h>
#include <lib/assert.h>

extern list_t view_show_list_head;
extern uint16_t *view_id_map;
extern spinlock_t view_list_lock;

static view_refresh_block_t view_refresh_block = NULL;

static void ViewRefreshBlock8(view_t *view, int view_left, int view_top, int view_right, int view_bottom);
static void ViewRefreshBlock15(view_t *view, int view_left, int view_top, int view_right, int view_bottom);
static void ViewRefreshBlock16(view_t *view, int view_left, int view_top, int view_right, int vies_bottom);
static void ViewRefreshBlock24(view_t *view, int view_left, int view_top, int view_right, int view_bottom);
static void ViewRefreshBlock32(view_t *view, int view_left, int view_top, int view_right, int view_bottom);

int ViewRefreshInit()
{
    switch (view_screen.bit_per_pixel)
    {
    case 8:
        view_refresh_block = ViewRefreshBlock8;
        break;
    case 15:
        view_refresh_block = ViewRefreshBlock15;
        break;
    case 16:
        view_refresh_block = ViewRefreshBlock16;
        break;
    case 24:
        view_refresh_block = ViewRefreshBlock24;
        break;
    case 32:
        view_refresh_block = ViewRefreshBlock32;
        break;
    default:
        KPrint("[view] refesh init err,unknow args");
        return -1;
    }
    return 0;
}

// from bottom refresh to current view,update current view and lower view
void ViewRefreshRectFromBottom(view_t *view, int x, int y, uint32_t width, uint32_t height)
{
    return ViewRefreshFromBottom(view, x, y, x + width, y + height);
}

void ViewRefreshFromBottom(view_t *view, int left, int top, int right, int bottom)
{
    SpinLockDisInterrupt(&view->lock);
    if (view->z >= 0)
    {
        ViewRefreshMap(view->x + left, view->y + top, view->x + right, view->y + bottom, 0);
        ViewRefreshByZ(view->x + left, view->y + top, view->x + right, view->y + bottom, 0, view->z);
    }
    SpinUnlockEnInterrupt(&view->lock);
}

void ViewRefreshRect(view_t *view, int x, int y, uint32_t width, uint32_t height)
{
    ViewRefresh(view, x, y, x + width, y + height);
}



// only refresh current view,no update other view
void ViewRefresh(view_t *view, int left, int top, int right, int bottom)
{
    if (view->z >= 0)
    {
        ViewRefreshMap(view->x + left, view->y + top, view->x + right, view->y + bottom, view->z);
        ViewRefreshByZ(view->x + left, view->y + top, view->x + right, view->y + bottom, view->z, view->z);
    }
}

void ViewRefreshByZ(int left, int top, int right, int bottom, int z0, int z1)
{
    int view_left, view_top, view_right, view_bottom;

    // adjust by check
    if (left < 0)
        left = 0;
    if (top < 0)
        top = 0;
    if (right >= view_screen.width)
        right = view_screen.width;
    if (bottom >= view_screen.height)
        bottom = view_screen.height;

    view_t *view;
    SpinLockDisInterrupt(&view_list_lock);
    list_traversal_all_owner_to_next(view, &view_show_list_head, list)
    {
        if (view->z >= z0 && view->z <= z1)
        {
            // figure view rect
            view_left = left - view->x;
            view_top = top - view->y;
            view_right = right - view->x;
            view_bottom = bottom - view->y;
            // adjust view rect to fit view self size
            if (view_left < 0)
                view_left = 0;
            if (view_top < 0)
                view_top = 0;
            if (view_right >= view->width)
                view_right = view->width;
            if (view_bottom >= view->height)
                view_bottom = view->height;

            // callback refresh block function
            ViewRefreshBlock24(view, view_left, view_top, view_right, view_bottom);
        }
    }
    SpinUnlockEnInterrupt(&view_list_lock);
}

void ViewDumpMap(int left, int top, int right, int bottom)
{
    int x, y;
    KPrint("--view map dump[%d %d %d %d]-------\n", left, top, right, bottom);
    for (y = top; y < bottom; y++)
    {
        for (x = left; x < right; x++)
            KPrint("%d ", view_id_map[y * view_screen.width + x]);
        KPrint("\n");
    }
}

void ViewRefreshMap(int left, int top, int right, int bottom, int z0)
{
    int view_left, view_top, view_right, view_bottom;
    int screen_x, screen_y;
    int view_x, view_y;
    view_color_t *color;

    // adjust area
    if (left < 0)
        left = 0;
    if (top < 0)
        top = 0;
    if (right < 0)
        right = view_screen.width;
    if (bottom < 0)
        bottom = view_screen.height;

    view_t *view=NULL;
    uint32_t *src=NULL;
    uint16_t *map=NULL;
    SpinLockDisInterrupt(&view_list_lock);
    // refresh view hight from z0-top
    list_traversal_all_owner_to_next(view, &view_show_list_head, list)
    {
        if (view->z >= z0)
        {
            // get view off
            view_left = left - view->x;
            view_top = top - view->y;
            view_right = right - view->x;
            view_bottom = bottom - view->y;

            if (view_left < 0)
                view_left = 0;
            if (view_top < 0)
                view_top = 0;
            if (view_right >= view->width)
                view_right = view->width;
            if (view_bottom >= view->height)
                view_bottom = view->height;

            color = (view_color_t *)view->section->addr;

            // view_top
            screen_y = view->y + view_top;
            if (screen_y < 0)
            {
                view_top += -screen_y;
            }
            if (screen_y >= view_screen.height)
                continue;
            // view_bottom
            screen_y = view->y + view_bottom;
            if (screen_y >= view_screen.height)
            {
                view_bottom -= screen_y - view_screen.height;
            }

            // view_left
            screen_x = view->x + view_left;
            if (screen_x < 0)
            {
                view_left += -screen_x;
            }
            if (screen_x >= view_screen.width)
                continue;
            // view_right
            screen_x = view->x + view_right;
            if (screen_x < 0)
            {
                view_right -= screen_x - view_screen.width;
            }

            //KPrint("[view] refresh view %d z %d view->x %d view->y %d view left %d top %d right %d bottom %d\n", view->id, view->z, view->x, view->y, view_left, view_top, view_right, view_bottom);
            // do map
            for (view_y = view_top; view_y < view_bottom; view_y++)
            {
                screen_y = view->y + view_y;
                src = &((uint32_t *)color)[view_y * view->width];
                map = &view_id_map[(screen_y * view_screen.width + view->x)];
              
                for (view_x = view_left; view_x < view_right; view_x++)
                {
                    if ((src[view_x] >> 24) & 0xff)
                    {
                        map[view_x] = view->z;
                    }
                }
            }
        }
    }
    SpinUnlockEnInterrupt(&view_list_lock);
}

static inline void ViewRefreshBlock8(view_t *view, int view_left, int view_top, int view_right, int view_bottom)
{
    int screen_y;
    int view_x;
    int view_y;

    uint8_t *dst;
    uint32_t *src;
    uint16_t *map;

    assert(view_id_map);

    for (view_y = view_top; view_y < view_bottom; view_y++)
    {
        dst = &((uint8_t *)view_screen.vram)[(view_screen.width * (view_y + view->y) + view->x)];
        src = &((uint32_t *)(view->section->addr))[view_y * view->width];

        map = &view_id_map[(view_y + view->y) * view_screen.width + view->x];

        for (view_x = view_left; view_x < view_right; view_x++)
        {
            if (map[view_x] == view->z)
            {
                dst[view_x] = (uint8_t)((src[view_x] & 0xC0) >> 6) | ((src[view_x] & 0xE000) >> 11) | ((src[view_x] & 0xE00000) >> 16);
            }
        }
    }
}

static inline void ViewRefreshBlock16(view_t *view, int view_left, int view_top, int view_right, int view_bottom)
{
    int screen_y;
    int view_x;
    int view_y;

    uint8_t *dst;
    uint32_t *src;
    uint16_t *map;

    assert(view_id_map);

    for (view_y = view_top; view_y < view_bottom; view_y++)
    {
        dst = &((uint8_t *)view_screen.vram)[(view_screen.width * (view_y + view->y) + view->x) * 2];
        src = &((uint32_t *)(view->section->addr))[view_y * view->width];

        map = &view_id_map[(view_y + view->y) * view_screen.width + view->x];

        for (view_x = view_left; view_x < view_right; view_x++)
        {
            if (map[view_x] == view->z)
            {
                dst[view_x] = (uint16_t)((src[view_x] & 0xF8) >> 3) | ((src[view_x] & 0xFC00) >> 5) | ((src[view_x] & 0xF80000) >> 8);
            }
        }
    }
}

static inline void ViewRefreshBlock15(view_t *view, int view_left, int view_top, int view_right, int view_bottom)
{
    int screen_y;
    int view_x;
    int view_y;

    uint8_t *dst;
    uint32_t *src;
    uint16_t *map;

    assert(view_id_map);

    for (view_y = view_top; view_y < view_bottom; view_y++)
    {
        dst = &((uint8_t *)view_screen.vram)[(view_screen.width * (view_y + view->y) + view->x) * 2];
        src = &((uint32_t *)(view->section->addr))[view_y * view->width];

        map = &view_id_map[(view_y + view->y) * view_screen.width + view->x];

        for (view_x = view_left; view_x < view_right; view_x++)
        {
            if (map[view_x] == view->z)
            {
                dst[view_x] = (uint16_t)((src[view_x] & 0xF8) >> 3) | ((src[view_x] & 0xF800) >> 6) | ((src[view_x] & 0xF80000) >> 9);
            }
        }
    }
}

static inline void ViewRefreshBlock24(view_t *view, int view_left, int view_top, int view_right, int view_bottom)
{
    int screen_y;
    int view_x;
    int view_y;

    uint8_t *dst;
    uint32_t *src;
    uint16_t *map;

    assert(view_id_map);
    //Print("[view] refresh to screen\n view x %d y %d left %d right %d top %d bottom %d\n", view->x, view->y, view_left, view_right, view_top, view_bottom);

    for (view_y = view_top; view_y < view_bottom; view_y++)
    {
        screen_y = view->y + view_y;
        dst = &((uint8_t *)view_screen.vram)[(view_screen.width * screen_y + view->x) * 3];
        src = &((uint32_t *)view->section->addr)[view_y * view->width];
        map = &view_id_map[(screen_y * view_screen.width + view->x)];

        for (view_x = view_left; view_x < view_right; view_x++)
        {
            if (map[view_x] == view->z)
            {
                dst[view_x * 3 + 0] = src[view_x] & 0xFF;
                dst[view_x * 3 + 1] = (src[view_x] & 0xFF00) >> 8;
                dst[view_x * 3 + 2] = (src[view_x] & 0xFF0000) >> 16;
            }
        }
    }
}

static inline void ViewRefreshBlock32(view_t *view, int view_left, int view_top, int view_right, int view_bottom)
{
    int screen_y;
    int view_x;
    int view_y;

    uint8_t *dst;
    uint32_t *src;
    uint16_t *map;

    assert(view_id_map);

    for (view_y = view_top; view_y < view_bottom; view_y++)
    {
        dst = &((uint32_t *)view_screen.vram)[(view_screen.width * (view_y + view->y) + view->x)];
        src = &((uint32_t *)(view->section->addr))[view_y * view->width];

        map = &view_id_map[(view_y + view->y) * view_screen.width + view->x];

        for (view_x = view_left; view_x < view_right; view_x++)
        {
            if (map[view_x] == view->z)
            {
                dst[view_x] = src[view_x];
            }
        }
    }
}
